﻿using Foundation.Core;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.ChangeTracking;

namespace Foundation.EntityFrameworkCore
{
    public class EntityTrackingContainer : IEntityTrackingDbContextContainer, IEntityTrackingContainer, ITransientDependency
    {
        protected List<IEntityEntryTracker> EntryTrackers { get; set; } = new List<IEntityEntryTracker>(1);

        protected List<DbContext> DbContexts { get; set; } = new List<DbContext>(1);

        public void AddDbContext(DbContext dbContext)
        {
            DbContexts.Add(dbContext);
            dbContext.ChangeTracker.Tracked += OnEntityTracked;
            dbContext.ChangeTracker.StateChanged += OnStateChanged;
        }

        public void AddTracker(IEntityTracker entityTracker)
        {
            if (entityTracker is IEntityEntryTracker entryTracker)
            {
                if (EntryTrackers.Contains(entryTracker)) return;

                if (entityTracker is IEntityTrackingDbContextSetter setter)
                {
                    setter.SetDbContextGetter(() => DbContexts);
                }

                EntryTrackers.Add(entryTracker);
            }
        }

        public bool RemoveTracker(IEntityTracker entityTracker)
        {
            if (entityTracker is IEntityEntryTracker entryTracker)
            {
                return EntryTrackers.Remove(entryTracker);
            }

            return false;
        }

        public void Dispose()
        {
            EntryTrackers.Clear();

            foreach (var c in DbContexts)
            {
                c.ChangeTracker.Tracked -= OnEntityTracked;
                c.ChangeTracker.StateChanged -= OnStateChanged;
            }

            DbContexts.Clear();
        }

        public void OnSaveChangesFailed(object sender, SaveChangesFailedEventArgs args)
        {
            for (int i = EntryTrackers.Count - 1; i >= 0; i--)
            {
                EntryTrackers[i].SaveChangesFailed(args);
            }
        }

        protected void OnEntityTracked(object sender, EntityTrackedEventArgs args)
        {
            for (int i = EntryTrackers.Count - 1; i >= 0; i--)
            {
                EntryTrackers[i].Tracked(args);
            }
        }

        protected void OnStateChanged(object sender, EntityStateChangedEventArgs args)
        {
            for (int i = EntryTrackers.Count - 1; i >= 0; i--)
            {
                EntryTrackers[i].StateChanged(args);
            }
        }
    }
}
